use std::sync::Arc;

use nacos_sdk::api::{
    config::{ConfigChangeListener, ConfigResponse, ConfigService, ConfigServiceBuilder},
    constants,
    naming::{NamingService, NamingServiceBuilder, ServiceInstance},
    props::ClientProps,
};

pub struct NacoService {
    config_service: Box<dyn ConfigService>,
    naming_service: Box<dyn NamingService>,
}

impl NacoService {
    pub fn new() -> Self {
        NacoService {
            config_service: Self::build_config_service(),
            naming_service: Self::build_naming_service(),
        }
    }

    pub async fn get_config(&self) -> String {
        let config_resp = self
            .config_service
            .get_config(
                "stock-service.yml".to_string(),
                constants::DEFAULT_GROUP.to_string(),
            )
            .await;

        match config_resp {
            Ok(config_resp) => config_resp.content().to_string(),
            Err(_) => panic!("Failed to get configuration"),
        }
    }

    pub async fn listen_config(&self) {
        let listen = self
            .config_service
            .add_listener(
                "stock-service.yml".to_string(),
                constants::DEFAULT_GROUP.to_string(),
                Arc::new(MyConfigChangeListener {}),
            )
            .await;

        match listen {
            Ok(_) => println!("Listening the config success"),
            Err(err) => println!("listen config error {:?}", err),
        }
    }

    pub async fn register_instance(&self) -> ServiceInstance {
        let service_instance = ServiceInstance {
            ip: "127.0.0.1".to_string(),
            port: 8089,
            ..Default::default()
        };

        let _ = self
            .naming_service
            .register_instance(
                "stock-service".to_string(),
                Some(constants::DEFAULT_GROUP.to_string()),
                service_instance.to_owned(),
            )
            .await;

        service_instance
    }

    pub async fn deregister_instance(&self, service_instance: ServiceInstance) {
        let _ = self.naming_service.deregister_instance(
            "stock-service".to_string(),
            Some(constants::DEFAULT_GROUP.to_string()),
            service_instance,
        );
    }

    pub async fn get_service_info(&self, service_name: &str) -> (String, i32) {
        match self
            .naming_service
            .select_one_healthy_instance(
                service_name.to_string(),
                Some(constants::DEFAULT_GROUP.to_string()),
                vec![],
                false,
            )
            .await
        {
            Ok(service_instance) => (service_instance.ip, service_instance.port),
            Err(_) => panic!("Failed to get service instance: {}", service_name),
        }
    }

    fn build_config_service() -> Box<dyn ConfigService> {
        match ConfigServiceBuilder::new(
            ClientProps::new()
                .server_addr("127.0.0.1:8648")
                .app_name("stock_service"),
        )
        .build()
        {
            Ok(config_service) => Box::new(config_service),
            Err(_) => panic!("Failed to create nacos config service"),
        }
    }

    fn build_naming_service() -> Box<dyn NamingService> {
        match NamingServiceBuilder::new(
            ClientProps::new()
                .server_addr("127.0.0.1:8648")
                .app_name("stock_service"),
        )
        .build()
        {
            Ok(naming_service) => Box::new(naming_service),
            Err(_) => panic!("Failed to create nacos naming service"),
        }
    }
}

struct MyConfigChangeListener;

impl ConfigChangeListener for MyConfigChangeListener {
    fn notify(&self, config_resp: ConfigResponse) {
        println!("Config changed: {}", config_resp.content());
    }
}
